import { ref, computed, onMounted, onUnmounted, nextTick, watch, readonly } from 'vue'

export interface VirtualScrollItem {
  id: string
  height?: number
  data: any
}

export interface VirtualScrollOptions {
  itemHeight: number
  containerHeight: number
  buffer: number
  threshold: number
  overscan: number
}

export interface VirtualScrollState {
  startIndex: number
  endIndex: number
  offsetY: number
  totalHeight: number
  visibleItems: VirtualScrollItem[]
}

export function useVirtualScroll<T extends VirtualScrollItem>(
  items: T[],
  options: Partial<VirtualScrollOptions> = {}
) {
  const defaultOptions: VirtualScrollOptions = {
    itemHeight: 80,
    containerHeight: 400,
    buffer: 5,
    threshold: 100,
    overscan: 3
  }

  const config = { ...defaultOptions, ...options }
  
  // Refs
  const containerRef = ref<HTMLElement>()
  const scrollTop = ref(0)
  const containerHeight = ref(config.containerHeight)
  const itemHeights = ref<Map<string, number>>(new Map())
  const isScrolling = ref(false)
  const scrollTimer = ref<number>()

  // Computed properties
  const totalHeight = computed(() => {
    if (itemHeights.value.size === 0) {
      return items.length * config.itemHeight
    }
    
    let height = 0
    for (let i = 0; i < items.length; i++) {
      const item = items[i]
      height += itemHeights.value.get(item.id) || config.itemHeight
    }
    return height
  })

  const visibleRange = computed(() => {
    const start = Math.floor(scrollTop.value / config.itemHeight)
    const visibleCount = Math.ceil(containerHeight.value / config.itemHeight)
    
    const startIndex = Math.max(0, start - config.overscan)
    const endIndex = Math.min(items.length - 1, start + visibleCount + config.overscan)
    
    return { startIndex, endIndex }
  })

  const offsetY = computed(() => {
    let offset = 0
    for (let i = 0; i < visibleRange.value.startIndex; i++) {
      const item = items[i]
      offset += itemHeights.value.get(item.id) || config.itemHeight
    }
    return offset
  })

  const visibleItems = computed(() => {
    const { startIndex, endIndex } = visibleRange.value
    return items.slice(startIndex, endIndex + 1)
  })

  const virtualScrollState = computed<VirtualScrollState>(() => ({
    startIndex: visibleRange.value.startIndex,
    endIndex: visibleRange.value.endIndex,
    offsetY: offsetY.value,
    totalHeight: totalHeight.value,
    visibleItems: visibleItems.value
  }))

  // Methods
  const updateItemHeight = (itemId: string, height: number) => {
    if (height > 0) {
      itemHeights.value.set(itemId, height)
    }
  }

  const handleScroll = (event: Event) => {
    const target = event.target as HTMLElement
    scrollTop.value = target.scrollTop
    
    isScrolling.value = true
    
    if (scrollTimer.value) {
      clearTimeout(scrollTimer.value)
    }
    
    scrollTimer.value = window.setTimeout(() => {
      isScrolling.value = false
    }, 150)
  }

  const scrollToIndex = (index: number, behavior: ScrollBehavior = 'auto') => {
    if (!containerRef.value || index < 0 || index >= items.length) return
    
    let offset = 0
    for (let i = 0; i < index; i++) {
      const item = items[i]
      offset += itemHeights.value.get(item.id) || config.itemHeight
    }
    
    containerRef.value.scrollTo({
      top: offset,
      behavior
    })
  }

  const scrollToBottom = (behavior: ScrollBehavior = 'auto') => {
    if (!containerRef.value) return
    
    containerRef.value.scrollTo({
      top: totalHeight.value,
      behavior
    })
  }

  const scrollToTop = (behavior: ScrollBehavior = 'auto') => {
    if (!containerRef.value) return
    
    containerRef.value.scrollTo({
      top: 0,
      behavior
    })
  }

  const updateContainerHeight = () => {
    if (containerRef.value) {
      containerHeight.value = containerRef.value.clientHeight
    }
  }

  // Lifecycle
  const resizeObserver = ref<ResizeObserver>()

  onMounted(() => {
    if (containerRef.value) {
      updateContainerHeight()
      
      // Setup resize observer
      if (window.ResizeObserver) {
        resizeObserver.value = new ResizeObserver(() => {
          updateContainerHeight()
        })
        resizeObserver.value.observe(containerRef.value)
      }
    }
  })

  onUnmounted(() => {
    if (scrollTimer.value) {
      clearTimeout(scrollTimer.value)
    }
    
    if (resizeObserver.value) {
      resizeObserver.value.disconnect()
    }
  })

  // Watch for items changes to reset heights if needed
  watch(() => items.length, (newLength, oldLength) => {
    if (newLength < oldLength) {
      // Items were removed, clean up heights
      const currentIds = new Set(items.map(item => item.id))
      const heightsToRemove: string[] = []
      
      itemHeights.value.forEach((_, id) => {
        if (!currentIds.has(id)) {
          heightsToRemove.push(id)
        }
      })
      
      heightsToRemove.forEach(id => {
        itemHeights.value.delete(id)
      })
    }
  })

  return {
    // Refs
    containerRef,
    scrollTop: readonly(scrollTop),
    isScrolling: readonly(isScrolling),
    
    // Computed
    virtualScrollState: readonly(virtualScrollState),
    totalHeight: readonly(totalHeight),
    visibleItems: readonly(visibleItems),
    
    // Methods
    handleScroll,
    updateItemHeight,
    scrollToIndex,
    scrollToBottom,
    scrollToTop,
    updateContainerHeight
  }
}